
/*
FPGA 上面的 dbg-port 实际上是一个 qspi slave 接口.
按照 ssi, spi bridge feature 章节来使用.

本文件是利用 cv1800b 里面的的 spi-nor-flash 控制器作为 master 接口.
产生 spi-bridge 需要的响应时序, 完成对 soc 内部寄存器的读写需求.
*/



#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include "soc.h"
#include "dbg_cli.h"





void qspi_state_reset( cvi_spinor_regs_t * psnor )
{
    uint32_t temp;

    temp = psnor->SPI_CTRL;
    temp = temp | 0x200000;
    psnor->SPI_CTRL = temp;

    //
    //temp = psnor->SPI_CTRL;
    //printf( "reset = 0x%08x\n", temp );
    test_sleep( 400 );
    return;
}



extern int log_printf( const char * fmt, ... );


/*
等待 spi 会话结束.
wcycle 可以指定需要等待 spi 的时钟数量.
当前 spi clock 配置的频率是 12.5Mhz.
当前 cpu clock 配置是 500Mhz.
500 / 12.5 = 40.
*/
__attribute__((__noinline__)) int qspi_wait_finish( cvi_spinor_regs_t * psnor, uint32_t wcycle )
{
    uint32_t intsts;
    uint64_t cycold;
    uint64_t cycnew;
    uint64_t cycwait;

    cycold = __get_MTIME();
    cycwait = (uint64_t)wcycle * 80;

#if 0
    /* wait complete */
    while ( true )  {

        intsts = psnor->INT_STS;
        if ( intsts & 0x1 )  {
            intsts = psnor->TRAN_CSR;
            log_printf( "TCSR = %x\n",  intsts );
            return 0;
        }

        cycnew = __get_MTIME();
        if ( (cycnew - cycold) > cycwait )  {
            break;
        }
    }


    // try again.
    __disable_irq();
    intsts = psnor->INT_STS;
    if ( intsts & 0x1 )  {
        __enable_irq();
        return 1;
    } else {
        __enable_irq();
        return 122;
    }

#else

    /* wait complete */
    while ( true )  {

        intsts = psnor->TRAN_CSR;
        if ( 0 == (intsts & 0x8000) )  {
            // intsts = psnor->INT_STS;
            // log_printf( "int = %x\n",  intsts );
            return 0;
        }

        cycnew = __get_MTIME();
        if ( (cycnew - cycold) > cycwait )  {
            break;
        }
    }
    
    return 13;

#endif


}





/*
特殊场景.
芯片上电之后, 默认 单线模式.
尝试写 sysctl 模块的 0x200010 寄存器等于 5.
就可以让 dbgport 切换到 4 线模式.
所以仅需要支持 单写寄存器.
*/
int qspi_1wire_write( cvi_spinor_regs_t * psnor, uint32_t addr, uint32_t value )
{
    int iret;
    uint32_t inst;
    uint32_t temp;
    uint32_t ffss;

    /* cmd + addr : 4 bytes */
    inst = 0x80;
    inst = (inst << 24) | (addr >> 2);

    /* trig_level = 4, with_cmd = 1, addr_bn=3, bus = 0, tx_only */
    temp = 0x2B02;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = 4;

    /* push : inst + addr */
    psnor->FF32 = __builtin_bswap32( inst );

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;

    /* check WR fifo int */
    while ( true )  {

        ffss = psnor->FIFO_PT;
        if ( (ffss & 0xF) > 2 )  {
            continue;
        }

        // only 1 word data
        psnor->FF32 = __builtin_bswap32( value );
        break;
    }

    /* wait complete */
    iret = qspi_wait_finish( psnor, 32 );
    if ( iret != 0 )  {
        printf( "1wire, write, timout..\n" );
        return 123;
    }

    return 0;
}


/*
0xC0,0xC8,0xD0 : get write status
0x40,0x48,0x50 : get read status
*/
int bl2_dbg_qstatus( void * pctx, int argc, const char * argv[] )
{
    int iret;
    uint8_t inst;
    uint32_t temp;
    uint32_t status;
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;


    if ( argc < 3 )  {
        goto usage;
    }

    if ( 0 == strcmp( argv[1], "write") )  {
        inst = 0xC0;
    } else if ( 0 == strcmp( argv[1], "read") )  {
        inst = 0x40;
    } else {
        goto usage;
    }

    iret = debug_str2uint( argv[2], &temp );
    if ( iret != 0 )  {
        printf( "dummy parse fmt err, %d\n", iret );
        goto usage;
    }

    if ( temp > 2 ) {
        printf( "dummy range err, %u\n", temp );
        goto usage;
    }

    inst = inst | (uint8_t)(temp << 3);

    /* qual, fast */
    temp = temp << 16;
    temp = temp | 0x129;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = 1;
    
    /* push : inst only */
    psnor->FF8 = inst;

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;

    while ( true )  {

        status = psnor->INT_STS;
        if ( status & 0x1 )  {
            break;
        }
    }

    temp = psnor->FIFO_PT;
    temp = temp & 0xF;
    printf( "fifo = %u\n", temp );

    temp = psnor->FF8;
    printf( "rval = %x\n", temp );
    return 0;

usage:
    printf( "usage:\n" );
    printf( "\t%s <write | read> <dummy>\n", argv[0] );
    printf( "\tdummy range [0-2]\n" );
    return 0;
}


/**/
int qspi_write_status( cvi_spinor_regs_t * psnor, uint8_t * psts )
{
    int iret;
    uint8_t inst;
    uint32_t temp;

    // dummy cycle = 2
    temp = 2;
    inst = 0xC0 | (uint8_t)(temp << 3);

    /* qual, fast */
    temp = temp << 16;
    temp = temp | 0x129;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = 1;

    /* push : inst only */
    psnor->FF8 = inst;

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;

    /* wait complete */
    iret = qspi_wait_finish( psnor, 62 );
    if ( iret != 0 )  {
        printf( "status, write, timout..\n" );
        return 123;
    }

    // result
    *psts = psnor->FF8;
    return 0;
}


/*
如果环境稳定, 可以不用检查 status .
tlen 支持: 1,4,8,16,32,64
addr 需要地址对齐在 4 字节.
*/

int qspi_write_data( cvi_spinor_regs_t * psnor, uint32_t addr, int tlen, uint32_t * pdat )
{
    int iret;
    int offs;
    uint32_t inst;
    uint8_t elen;
    uint32_t temp;
    uint32_t ffss;

    // 1,4,8,16,32,64 >> 0,1,2,3,4,5
    elen = __builtin_ctz(tlen);
    if ( elen >= 2 )  {
        elen = elen - 1;
    }

    //
    inst = 0x80 | elen;
    inst = (inst << 24) | (addr >> 2);

    /* trig_level = 4, addr_bn = 4, bus = qual, tx_only */
    temp = 0x242A;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = tlen << 2;

    /* push : inst + addr */
    psnor->FF32 = __builtin_bswap32( inst );

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;

    /* check WR fifo int */
    offs = 0;
    while ( true )  {

        ffss = psnor->FIFO_PT;
        if ( (ffss & 0xF) > 4 )  {
            continue;
        }

        // only 1 word data
        psnor->FF32 = __builtin_bswap32( pdat[offs] );
        offs += 1;

        if ( offs == tlen )  {
            break;
        }
    }

    /* wait complete */
    iret = qspi_wait_finish( psnor, 104 );
    if ( iret != 0 )  {
        printf( "data, write, timout..\n" );
        return 123;
    }

    // printf( "addr:%x,cnt:%d,val:%x\n", addr, tlen, pdat[0] );
    return 0;
}



int qspi_write_allstep( cvi_spinor_regs_t * psnor, uint32_t addr, int tlen, uint32_t * pdat )
{
    int tmos;
    uint8_t status;

    // 
    qspi_write_data( psnor, addr, tlen, pdat );

    tmos = tlen + 2;
    for ( int i=0; i<tmos; i++ )  {
    
        qspi_write_status( psnor, &status );

        if ( status == 0x80 )  {
            return 0;
        } else if ( status == 0xC0 )  {
            return 32;
        }

    }

    return 31;
}



/*
addr, data, length :
length = 1,4,8,16,32,64
*/
int bl2_dbg_qwrite( void * pctx, int argc, const char * argv[] )
{
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;
    int iret;
    uint32_t addr;
    uint32_t data;
    uint32_t tlen;
    uint32_t tary[64];
    

    if ( argc < 4 )  {
        goto usage;
    }

    iret = debug_str2uint( argv[1], &addr );
    if ( iret != 0 )  {
        printf( "addr parse fail, %d\n", iret );
        goto usage;
    }

    iret = debug_str2uint( argv[2], &data );
    if ( iret != 0 )  {
        printf( "data parse fail, %d\n", iret );
        goto usage;
    }

    iret = debug_str2uint( argv[3], &tlen );
    if ( iret != 0 )  {
        printf( "length parse fail, %d\n", iret );
        goto usage;
    }
    
    // check addr align ?
    if ( ((addr & 0x3) != 0)  || (addr > 0x3FFFFFF) )  {
        printf( "addr range/align fail\n" );
        goto usage;
    }

    // check tlen range
    if ( (tlen > 64) || (tlen == 2) )  {
        printf( "length range fail\n" );
        goto usage;
    }

    iret = __builtin_popcount( tlen );
    if ( iret != 1  )  {
        printf( "length range fail\n" );
        goto usage;
    }

    for ( int i=0; i<(int)tlen; i++ )  {
        tary[i] = data + i;
    }

    iret = qspi_write_data( psnor, addr, (int)tlen, tary );
    if ( iret != 0 )  {
        printf( "write data, ret = %d\n", iret );
    }

    return 0;

usage:
    printf( "usage:\n" );
    printf( "\t%s <addr> <data> <length>\n", argv[0] );
    printf( "\tlength : 1/4/8/16/32/64\n" );
    return 0;
}



/*
INST, ADDR :
tx only, 
*/
int qspi_read_request( cvi_spinor_regs_t * psnor, uint32_t addr, uint8_t elen )
{
    int iret;
    uint32_t inst;
    uint32_t temp;

    // INST, ADDR.
    inst = 0x20 | (elen & 0x7);
    inst = (inst << 24) | (addr >> 2);

    /* qual, fast, tx_only */
    temp = 0x22A;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = 2;
    
    /* push : inst only */
    psnor->FF32 = __builtin_bswap32( inst );

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;
    
    /* wait complete */
    iret = qspi_wait_finish( psnor, 100 );
    if ( iret != 0 )  {
        printf( "request, read, timout..\n" );
        return 123;
    }

    return 0;
}


/*
send(INST, DUMMY), recv(STATUS)
*/
int qspi_read_status( cvi_spinor_regs_t * psnor, uint8_t * psts )
{
    int iret;
    uint8_t inst;
    uint32_t dummy;
    uint32_t temp;

    /**/
    dummy = 2;
    inst = 0x40 | (uint8_t)(dummy << 3);

    /* qual, fast, rx_only */
    temp = dummy << 16;
    temp = temp | 0x129;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = 1;
    
    /* push : inst only */
    psnor->FF8 = inst;

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;

    /* wait complete */
    iret = qspi_wait_finish( psnor, 64 );
    if ( iret != 0 )  {
        printf( "status, read, timout ...\n" );
        return 123;
    }

    *psts = psnor->FF8;
    return 0;
}



/*
send(INST, DUMMY), recv(DATA)
*/
int qspi_read_data( cvi_spinor_regs_t * psnor, int tlen, uint8_t elen, uint32_t * pdat )
{
    int iret;
    int offs;
    uint8_t inst;
    uint32_t dummy;
    uint32_t temp;
    uint32_t ffss;

    /**/
    dummy = 1;
    inst = (uint8_t)(dummy << 3) | (elen & 0x7);

    /* RX, ,   */
    temp = dummy << 16;
    temp = temp | 0x129;
    psnor->TRAN_CSR = temp;

    /* clear irqs and fifo */
    psnor->FIFO_PT = 0;
    psnor->CE_CTRL = 0;
    psnor->INT_STS = 0;

    /* trans multi words */
    psnor->TRAN_NUM = (tlen << 2);

    /* push inst, 8 bits */
    psnor->FF8 = inst;

    /* go = 1 */
    psnor->TRAN_CSR = temp | 0x8000;
    
    /* check RD fifo int */
    while ( true )  {

        ffss = psnor->INT_STS;
        psnor->INT_STS = 0;

        if ( ffss & 0x4 )  {
            break;
        }
    }

    offs = 0;
    while ( true )  {

        ffss = psnor->FIFO_PT;
        if ( (ffss & 0xF) >= 4 )  {
            pdat[offs] = __builtin_bswap32( psnor->FF32 );
            offs += 1;

            if ( offs >= tlen )  {
                break;
            }
        }
    }

    /* wait complete */
    iret = qspi_wait_finish( psnor, 800 );
    if ( iret != 0 )  {
        printf( "data, read, timout..\n" );
        return 123;
    }

    return 0;
}


int qspi_read_allstep( cvi_spinor_regs_t * psnor, uint32_t addr, int tlen, uint32_t * pary )
{
    int iret;
    int retry;
    uint8_t elen;
    uint8_t status;

    // 1,4,8,16,32,64 >> 0,1,2,3,4,5
    elen = __builtin_ctz(tlen);
    if ( elen >= 2 )  {
        elen = elen - 1;
    }

    // state reset..
    qspi_state_reset( psnor );

    // request
    iret = qspi_read_request( psnor, addr, elen );
    if ( iret != 0 )  {
        return 100 + iret;
    }


    retry = 0;
    while ( true )  {

        // state reset..
        // qspi_state_reset( psnor );

        // status
        iret = qspi_read_status( psnor, &status );
        if ( iret != 0 )  {
            return 200 + iret;
        }

        if ( status == 0x80 )  {
            break;
        }

        retry += 1;
        if ( retry >= (tlen + 2) ) {
            printf( "retry fail: %x\n", status );
            return 404;
        }
    }


    // state reset..
    qspi_state_reset( psnor );
    
    // read data
    iret = qspi_read_data( psnor, tlen, elen, pary );
    if ( iret != 0 )  {
        return 300 + iret;
    }

    // printf( "addr:%x,len:%d,try=%u\n", addr, tlen, retry );
    return 0;
}



/*
qread <addr> [len]

addr 需要 4 字节对齐.
len 如果不输入, 默认是 1, 最大值是 64.

1, 4, 8, 16, 32, 64

*/

int bl2_dbg_qread( void * pctx, int argc, const char * argv[] )
{
    int iret;
    uint32_t addr;
    uint32_t tlen;
    uint32_t tary[64];
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;

    if ( argc < 2 )  {
        goto usage;
    }

    iret = debug_str2uint( argv[1], &addr );
    if ( iret != 0 )  {
        printf( "addr parse fail, %d\n", iret );
        goto usage;
    }

    // range, align : check
    if ( ((addr & 0x3) != 0)  || (addr > 0x3FFFFFF) )  {
        printf( "addr range/align fail\n" );
        goto usage;
    }

    if ( argc >= 3 )  {

        iret = debug_str2uint( argv[2], &tlen );
        if ( iret != 0 )  {
            printf( "length parse fail, %d\n", iret );
            goto usage;
        }

        //
        if ( (tlen > 64) || (tlen == 2) )  {
            printf( "length range fail\n" );
            goto usage;
        }

        iret = __builtin_popcount( tlen );
        if ( iret != 1  )  {
            printf( "length range fail\n" );
            goto usage;
        }

    } else {
        tlen = 1;
    }


    //
    iret = qspi_read_allstep( psnor, addr, tlen, tary );
    if ( iret != 0 )  {
        printf( "qspi read fail, %d\n", iret );
        return iret;
    }


    // dump ..
    for ( int i=0; i<tlen; i++ )  {

        // prefix addr.
        if ( (i % 4) == 0 )  {
            printf( "%07x: ", addr + (i << 2) );
        }

        // print data
        printf( "%08x ", tary[i] );

        // line end.
        if ( (i % 4) == 3 )  {
            printf( "\n" );
        }
    }

    printf( "\n" );
    return 0;


usage:
    printf( "usage:\n" );
    printf( "\t%s <addr> [length]\n", argv[0] );
    return 0;
}




/*
流水线方式, 32 words 接力.
*/

int qspi_read_pipe( cvi_spinor_regs_t * psnor, uint32_t addr, uint32_t * pary )
{
    int iret;
    int retry;
    uint8_t status;

    // request, 64
    iret = qspi_read_request( psnor, addr, 5 );
    if ( iret != 0 )  {
        return 100 + iret;
    }

    retry = 0;
    while ( true )  {
        // status
        iret = qspi_read_status( psnor, &status );
        if ( iret != 0 )  {
            return 200 + iret;
        }

        if ( status == 0x80 )  {
            break;
        }

        retry += 1;
    }
    

    // read data
    iret = qspi_read_data( psnor, 32, 4, pary );
    if ( iret != 0 )  {
        return 300 + iret;
    }

    // request, 32
    iret = qspi_read_request( psnor, addr + (64 * 4), 4 );
    if ( iret != 0 )  {
        return 100 + iret;
    }
    
    qspi_read_status( psnor, &status );

    // read data
    iret = qspi_read_data( psnor, 32, 4, pary+32 );
    if ( iret != 0 )  {
        return 300 + iret;
    }

    // request, 32
    iret = qspi_read_request( psnor, addr + (96 * 4), 4 );
    if ( iret != 0 )  {
        return 100 + iret;
    }
    
    qspi_read_status( psnor, &status );

    // read data
    iret = qspi_read_data( psnor, 32, 4, pary+64 );
    if ( iret != 0 )  {
        return 300 + iret;
    }

    //
    printf( "status = %u\n", status );
    printf( "retry = %u\n", retry );
    return 0;
}




int bl2_dbg_pread( void * pctx, int argc, const char * argv[] )
{
    int iret;
    uint32_t addr;
    uint32_t tary[128];
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;

    if ( argc < 2 )  {
        goto usage;
    }

    iret = debug_str2uint( argv[1], &addr );
    if ( iret != 0 )  {
        printf( "addr parse fail, %d\n", iret );
        goto usage;
    }

    // range, align : check
    if ( ((addr & 0x3) != 0)  || (addr > 0x3FFFFFF) )  {
        printf( "addr range/align fail\n" );
        goto usage;
    }
    

    //
    iret = qspi_read_pipe( psnor, addr, tary );
    if ( iret != 0 )  {
        printf( "pipe read fail, %d\n", iret );
        return iret;
    }


    // dump ..
    for ( int i=0; i<96; i++ )  {

        // prefix addr.
        if ( (i % 4) == 0 )  {
            printf( "%07x: ", addr + (i << 2) );
        }

        // print data
        printf( "%08x ", tary[i] );

        // line end.
        if ( (i % 4) == 3 )  {
            printf( "\n" );
        }
    }

    printf( "\n" );
    return 0;

usage:
    printf( "usage:\n" );
    printf( "\t%s <addr>\n", argv[0] );
    return 0;
}


/*
fifo 最大深度 8 bytes.
会根据 write 操作的的 mask 来 push 数据.
如果 word 写操作, 那么 push 4 bytes.
但是 byte 写操作, 只会 push 1 byte.

怀疑, 硬件不认识 half (uint16) 写操作.
*/

int bl2_dbg_nor( void * pctx, int argc, const char * argv[] )
{
    int iret;
    uint8_t status;
    uint32_t temp;
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;


    if ( argc < 2 )  {
        
        // (300 / (2 * (div+1))) = 300/24 = 12.5Mhz.
        temp = 11;
        
    } else {

        iret = debug_str2uint( argv[1], &temp );
        if ( iret != 0 )  {
            goto usage;
        }

    }


    // frame length = 8 bits
    temp = (temp & 0xfff) | 0x80000;

    /* default setting .*/
    psnor->DMMR_CTRL = 0;           /* disable */
    psnor->SPI_CTRL = temp;         /* freq = 300 / (2 * (div + 1)) */
    psnor->INT_STS = 0;
    psnor->FIFO_PT = 0;             /* clear fifo */
    psnor->CE_CTRL = 0;
    
    test_sleep( 300 );


    /* try write reg 0x200010 = 5 */
    qspi_1wire_write( psnor, 0x200010, 5 );

    qspi_write_status( psnor, &status );
    printf( "w-status = %x\n", status );
    qspi_write_status( psnor, &status );
    printf( "w-status = %x\n", status );
    return 0;

usage:
    printf( "usage:\n" );
    printf( "\t%s [div]\n", argv[0] );
    return 0;
}



#if 0

/*
fifo 最大深度 8 bytes.
会根据 write 操作的的 mask 来 push 数据.
如果 word 写操作, 那么 push 4 bytes.
但是 byte 写操作, 只会 push 1 byte.

怀疑, 硬件不认识 half (uint16) 写操作.
*/

int bl2_dbg_nor( void * pctx, int argc, const char * argv[] )
{
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;

    /* default setting .*/
    psnor->DMMR_CTRL = 0;           /* disable */
    psnor->SPI_CTRL = 0x8001D;      /* div = 2 * (0x1D + 1) = 60, 300Mhz / 60 = 5Mhz  */
    psnor->INT_STS = 0;
    psnor->FIFO_PT = 0;             /* clear fifo */
    psnor->CE_CTRL = 0;

    return 0;
}


#endif


#if  1



/*
仅仅读 samp 模块的内部地址空间.
base = 0x150000,  64k bytes.
1, 4, 8, 16, 32, 64
*/
int dbg_qspi_read( cvi_spinor_regs_t * psnor, uint32_t addr, int tcnt, uint32_t * pdat )
{
    int iret;
    int offs = 0;
    

    while ( (tcnt - offs) >= 64 )  {
        
        iret = qspi_read_allstep( psnor, addr + (offs<<2), 64, &(pdat[offs]) );
        if ( iret != 0 )  {
            return iret;
        }

        offs += 64;
    }

    while ( (tcnt - offs) >= 32 )  {
        iret = qspi_read_allstep( psnor, addr + (offs<<2), 32, &(pdat[offs]) );
        if ( iret != 0 )  {
            return iret;
        }

        offs += 32;
    }

    while ( (tcnt - offs) >= 16 )  {
        iret = qspi_read_allstep( psnor, addr + (offs<<2), 16, &(pdat[offs]) );
        if ( iret != 0 )  {
            return iret;
        }

        offs += 16;
    }

    while ( (tcnt - offs) >= 8 )  {
        iret = qspi_read_allstep( psnor, addr + (offs<<2), 8, &(pdat[offs]) );
        if ( iret != 0 )  {
            return iret;
        }

        offs += 8;
    }

    while ( (tcnt - offs) >= 4 )  {
        iret = qspi_read_allstep( psnor, addr + (offs<<2), 4, &(pdat[offs]) );
        if ( iret != 0 )  {
            return iret;
        }

        offs += 4;
    }

    while ( (tcnt - offs) >= 1 )  {
        iret = qspi_read_allstep( psnor, addr + (offs<<2), 1, &(pdat[offs]) );
        if ( iret != 0 )  {
            return iret;
        }

        offs += 1;
    }

    return 0;
}


/*
tcnt : 表示后面 pdat 数组的长度, <=0 都没有意义.  最大值 128.
*/
int dbg_qspi_write( cvi_spinor_regs_t * psnor, uint32_t addr, int tcnt, uint32_t * pdat )
{
    int iret;
    int offs = 0;


    while ( (tcnt - offs) >= 64 )  {
        
        iret = qspi_write_allstep( psnor, addr + (offs<<2), 64, &(pdat[offs]) );
        if ( iret != 0 )  {
            return 6400 + iret;
        }

        offs += 64;
    }

    while ( (tcnt - offs) >= 32 )  {
        iret = qspi_write_allstep( psnor, addr + (offs<<2), 32, &(pdat[offs]) );
        if ( iret != 0 )  {
            return 3200 + iret;
        }

        offs += 32;
    }

    while ( (tcnt - offs) >= 16 )  {
        
        iret = qspi_write_allstep( psnor, addr + (offs<<2), 16, &(pdat[offs]) );
        if ( iret != 0 )  {
            return 1600 + iret;
        }

        offs += 16;
    }

    while ( (tcnt - offs) >= 8 )  {
        iret = qspi_write_allstep( psnor, addr + (offs<<2), 8, &(pdat[offs]) );
        if ( iret != 0 )  {
            return 800 + iret;
        }

        offs += 8;
    }

    while ( (tcnt - offs) >= 4 )  {
        iret = qspi_write_allstep( psnor, addr + (offs<<2), 4, &(pdat[offs]) );
        if ( iret != 0 )  {
            return 400 + iret;
        }

        offs += 4;
    }

    while ( (tcnt - offs) >= 1 )  {
        
        iret = qspi_write_allstep( psnor, addr + (offs<<2), 1, &(pdat[offs]) );
        if ( iret != 0 )  {
            return 100 + iret;
        }

        offs += 1;
    }

    return 0;
}


#else

int dbg_qspi_read( cvi_spinor_regs_t * psnor, uint32_t addr, int cntw, uint32_t * pdat )
{
    for ( int i=0; i<cntw; i++ )  {
        pdat[i] = addr + i;
    }
    return 0;
}


int dbg_qspi_write( cvi_spinor_regs_t * psnor, uint32_t addr, int cntw, uint32_t * pdat )
{
    printf( "qspi,write : %x, %d\n", addr, cntw );
    return 0;
}

#endif



/*
多次测试 data read 64.
确认 intstatus 的 bit-0 是否是 1.

参数表示 循环测试的次数.
*/
int bl2_dbg_dread( void * pctx, int argc, const char * argv[] )
{
    int iret;
    cvi_spinor_regs_t * psnor = (cvi_spinor_regs_t *)SPINOR_BASE;
    uint32_t cntt;
    uint32_t i;
    uint32_t temp;
    uint32_t tary[1024];
    uint8_t status;

    if ( argc < 2 )  {
        goto usage;
    }

    iret = debug_str2uint( argv[1], &cntt );
    if ( iret != 0 )  {
        printf( "cnt parse fail, %d\n", iret );
        goto usage;
    }
    
    /**/
    for ( i=0; i<cntt; i++ )  {

        // request
        iret = qspi_read_request( psnor, 0x55AA55, 5 );
        if ( iret != 0 )  {
            break;
        }

        // status
        iret = qspi_read_status( psnor, &status );
        if ( iret != 0 )  {
            break;
        }

        // status
        iret = qspi_read_status( psnor, &status );
        if ( iret != 0 )  {
            break;
        }

        // status
        iret = qspi_read_status( psnor, &status );
        if ( iret != 0 )  {
            break;
        }

        iret = qspi_read_data( psnor, 64, 5, tary );
        if ( iret != 0 )  {
            break;
        }

        if ( (i % 100) == 99 )  {
            printf( "#" );
        }
    }


    if ( iret != 0 )  {
        temp = psnor->INT_STS;
        printf( "\n INTS : 0x%x\n", temp );
    }

    // 
    printf( "\nloop cnt = %u (%x)\n", i, i );
    return 0;

usage:
    printf( "usage:\n" );
    printf( "\t%s <cnt>\n", argv[0] );
    return 0;
}

